﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;

namespace TD.bom
{
    /// <summary>
    /// Gestion du jeu (i.e. d'une partie)
    /// </summary>
    public class Game
    {
        protected static Game m_instance; // unique instance de la classe
        int m_lastFrameTick;


        #region Propriétés

        /// <summary>
        /// Carte
        /// </summary>
        public Map Map
        {
            get; set;
        }

        /// <summary>
        /// Horloge du jeu
        /// </summary>
        public Clock Clock
        {
            get; set;
        }

        /// <summary>
        /// Etat du jeu / partie
        /// </summary>
        public GameState State
        {
            get;
            set;
        }

        /// <summary>
        /// Unique instance de la classe (singleton)
        /// </summary>
        public static Game Instance
        {
            get
            {
                if (m_instance == null)
                {
                    m_instance = new Game();
                }

                return m_instance;
            }
        }

        /// <summary>
        /// Objet responsable de la mise en forme des éléments du jeu
        /// </summary>
        public IRenderer Renderer
        {
            get;
            protected set;
        }

        /// <summary>
        /// Argent disponible
        /// </summary>
        public int Cash
        {
            get;
            protected set;
        }

        /// <summary>
        /// Nombre de vie du joueur
        /// </summary>
        public int HP
        {
            get;
            protected set;
        }

        #endregion

        /// <summary>
        /// Constructeur protégé (singleton)
        /// </summary>
        protected Game() 
        {
            State = GameState.None;
            Clock = new Clock();
        }

        /// <summary>
        /// Initialise l'objet
        /// </summary>
        public void Initialize(IRenderer p_renderer)
        {
            Renderer = p_renderer;
        }

        /// <summary>
        /// Démarre une partie
        /// </summary>
        /// <param name="p_mapName">Nom du template de la carte sur laquelle démarrer une partie</param>
        public void Start(string p_mapName)
        {
            // Chargement de la carte
            Map = new Map(p_mapName);

            State = GameState.Running;

            Cash = 10;

            HP = 5;
            
            Clock.Reset();
            m_lastFrameTick = Environment.TickCount;
        }

        /// <summary>
        /// Met le jeu en pause
        /// </summary>
        public void Pause()
        {
            Clock.Pause();
        }

        /// <summary>
        /// Reprend le jeu
        /// </summary>
        public void Resume()
        {
            Clock.Resume();
            m_lastFrameTick = Environment.TickCount;
        }

        /// <summary>
        /// Joue une itération du jeu
        /// </summary>
        public void GameLoop()
        {
            // Tick auquel s'éxécute cette itération
            int l_thisFrameTick = Environment.TickCount;

            if (State == GameState.Running && !Clock.Paused)
            {
                // Calcul du temps depuis le dernier rendu
                int l_frameDuration = l_thisFrameTick - m_lastFrameTick;
                int l_tickCaughtUp = 0;

                // Rattrapage des itérations pour la physique du jeu
                while (l_tickCaughtUp < l_frameDuration)
                {
                    // On rattrape par tranche de 4ms
                    Clock.AddTime(4);
                    l_tickCaughtUp += 4;

                    // on lance une itération pour la physique du jeu
                    Map.DoPhysics();
                }


                // mise à jour du rendu
                Renderer.DoRendering();
            }

            // Sauvegarde du tick pour le prochain calcul
            m_lastFrameTick = l_thisFrameTick;
        }

        /// <summary>
        /// Appelé lorsqu'on perd un monstre
        /// </summary>
        /// <param name="p_monster">Mostre ayant atteint la sortie de la carte</param>
        public void OnMonsterEscaped(Monster p_monster)
        {
            HP -= -1;
        }
    }
}
